/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import fs from 'fs';
import path from 'path';

import { NewSigningOptions } from '../../../builder/inner-java-command-builder/new-signing-options.js';
import { SigningOptions } from '../../../builder/inner-java-command-builder/signing-options.js';
import { buildOptionPath } from '../../../common/build-option-path-info.js';
import { ProjectModel } from '../../../model/project/project-model.js';
import { ProjectBuildProfile } from '../../../options/build/project-build-profile.js';
import { SdkInfo } from '../../../sdk/sdk-info.js';
import { DecipherUtil } from '../../../utils/decipher-util.js';
import { OhosLogger } from '../../../utils/log/ohos-logger.js';
import { SdkVersion } from '../../../version/sdk-version.js';
import { CommandBuilder } from '../command-builder.js';
import { SignModel } from './sign-model.js';

import SigningConfigBuildOpt = ProjectBuildProfile.SigningConfigBuildOpt;
import MaterialBuildOpt = ProjectBuildProfile.MaterialBuildOpt;

/**
 * 签名的公共处理参数的Builder类
 *
 * @since 2022/1/21
 */
export abstract class CommonSignCommandBuilder implements CommandBuilder {
  protected _toolchainsComponentVersion: SdkVersion | undefined;
  protected _projectModel: ProjectModel;
  protected _signingConfig: SigningConfigBuildOpt;
  protected _sdkInfo: SdkInfo;
  protected _signModel: SignModel;
  protected _signingOptions: SigningOptions | NewSigningOptions;
  private _log: OhosLogger = OhosLogger.getLogger(CommonSignCommandBuilder.name);
  private _keyStorePwd = '';
  private _keyPwd = '';

  protected constructor(
    projectModel: ProjectModel,
    signingConfig: SigningConfigBuildOpt,
    sdkInfo: SdkInfo,
    signModel: SignModel,
    signingOptions: SigningOptions | NewSigningOptions,
  ) {
    this._projectModel = projectModel;
    this._signingConfig = signingConfig;
    this._sdkInfo = sdkInfo;
    this._signModel = signModel;
    this._signingOptions = signingOptions;
    const componentVersion = this._sdkInfo.getToolchainsComponentVersion();
    if (componentVersion !== undefined) {
      this._toolchainsComponentVersion = new SdkVersion(componentVersion);
    }
  }

  abstract getSignTool(): string;

  // override
  abstract initCommandParams(material: MaterialBuildOpt): void;

  /**
   * 通过初始化signOptions的配置,并添加公共的签名工具路径
   *
   * @return signCommand
   */
  getSignCommand(): string[] {
    const material = this._signingConfig.material;
    this.checkValidMaterial(material);
    this.initCommandParams(material);

    this._signingOptions.addCalledJarFile(this.getSignTool());

    return this._signingOptions.build();
  }

  protected getKeyStorePwd(): string {
    this._keyStorePwd = DecipherUtil.decryptPwd(path.resolve(this._signingConfig.material.storeFile, '..'), this._signingConfig.material.storePassword);
    return this._keyStorePwd;
  }

  public readKeyStorePwd(): string {
    return this._keyStorePwd;
  }

  protected getKeyPwd() {
    this._keyPwd = DecipherUtil.decryptPwd(path.resolve(this._signingConfig.material.storeFile, '..'), this._signingConfig.material.keyPassword);
    return this._keyPwd;
  }

  public readKeyPwd() {
    return this._keyPwd;
  }

  /**
   * 将相对路径转化为绝对路径, 并校验签名材料是否存在
   *
   * @param {ProjectBuildProfile.MaterialBuildOpt | undefined} material
   * @protected
   */
  protected checkValidMaterial(material: MaterialBuildOpt | undefined): void {
    if (material === undefined) {
      this._log.printErrorExit('THE_MATERIAL_HAS_NOT_BEEN_CONFIGURED_IN_SIGNINGCONFIGS', [this._projectModel.getProfilePath()]);
    }

    this.validateMaterial(material!.storeFile, 'storeFile');
    this.validateMaterial(material!.profile, 'profile');
    this.validateMaterial(material!.certpath, 'certpath');

    material!.storeFile = this.normalizePath(material!.storeFile);
    material!.profile = this.normalizePath(material!.profile);
    material!.certpath = this.normalizePath(material!.certpath);
  }

  /**
   * 转化签名材料的相对路径为绝对路径.
   * 1 若是绝对路径, 直接返回
   * 2 若是相对路径, 则加上build-profile.json5的目录拼成绝对路径
   *
   * @param {string} materialFilePath 配置签名材料的路径
   * @returns {string} 签名材料的绝对路径
   * @private
   */
  private normalizePath(materialFilePath: string): string {
    if (path.isAbsolute(materialFilePath)) {
      return materialFilePath;
    }
    return path.resolve(this._projectModel.getProjectDir(), materialFilePath);
  }

  private validateMaterial(materialFilePath: string, materialFileName: string): void {
    if (materialFilePath !== undefined && fs.existsSync(this.normalizePath(materialFilePath))) {
      return;
    }
    this._log.printErrorExit(
      'INVALID_%S_VALUE_MAKE_SURE_IT_IS_NOT_NULL_OR_EMPTY',
      [materialFileName, materialFilePath, buildOptionPath.getSignConfigPath(this._projectModel, materialFileName)]
    );
  }

  protected validateSignType(signType: string): void {
    if (this._signingConfig.type === undefined) {
      // default value
      this._signingConfig.type = 'OpenHarmony';
    }
    if (this._signingConfig.type !== signType) {
      this._log.printErrorExit('SIGNING_CONFIGURATION_%S_DOES_NOT_APP_TO_%S', [this._signingConfig.name, signType, this._projectModel.getProfilePath()]);
    }
  }
}
